package com.mindata.blockchain.socket.DGL.queue;

import cn.hutool.core.bean.BeanUtil;
import com.mindata.blockchain.ApplicationContextProvider;
import com.mindata.blockchain.block.block.Block;
import com.mindata.blockchain.block.block.DGLBlock;
import com.mindata.blockchain.common.AppId;
import com.mindata.blockchain.common.Commonvalue;
import com.mindata.blockchain.common.timer.TimerManager;
import com.mindata.blockchain.core.event.AddBlockEvent;
import com.mindata.blockchain.core.event.DGLAddBlocksEvent;
import com.mindata.blockchain.core.manager.DbBlockManager;
import com.mindata.blockchain.core.sqlite.SqliteManager;
import com.mindata.blockchain.socket.DGL.event.DGLMsgPrepareEvent;
import com.mindata.blockchain.socket.DGL.msg.DGLVoteMsg;
import com.mindata.blockchain.socket.DGL.msg.DGLVotePreMsg;
import com.mindata.blockchain.socket.common.msg.BaseMsg;
import com.mindata.blockchain.socket.common.msg.MsgType;
import com.mindata.blockchain.socket.common.queue.base.BaseMsgQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * preprepare消息的存储，但凡收到请求生成Block的信息，都在这里处理
 *
 * @author wuweifeng wrote on 2018/4/23.
 */
@Component
public class DGLPreMsgQueue extends BaseMsgQueue {
    @Resource
    private SqliteManager sqliteManager;
    @Resource
    private DGLPrepareMsgQueue dglPrepareMsgQueue;
    @Resource
    private ApplicationEventPublisher eventPublisher;

    private ConcurrentHashMap<String, DGLVotePreMsg> dglVotePreMsgConcurrentHashMap = new ConcurrentHashMap<>();

    private DbBlockManager dbBlockManager = ApplicationContextProvider.getBean(DbBlockManager.class);
    private Commonvalue commonvalue = ApplicationContextProvider.getBean(Commonvalue.class);

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    protected void push(BaseMsg msg) {
        int concensusindex = (dbBlockManager.getLastBlockNumber() + 1) % commonvalue.getGroupsize();
        if (msg instanceof DGLVoteMsg && concensusindex != commonvalue.getNodeIndex()) {
            logger.error("不是leader 不能参与 global共识");
            return;
        }
        logger.error("DGLPreMsgQueue收到了dgl的preprepare消息");
        //该队列里的是votePreMsg
        DGLVotePreMsg dglVotePreMsg = (DGLVotePreMsg) msg;
        String hash = dglVotePreMsg.getHash();
        //避免收到重复消息
        if (dglVotePreMsgConcurrentHashMap.containsKey(hash)) {
            return;
        }
        //但凡是能进到该push方法的，都是通过基本校验的，但在并发情况下可能会相同number的block都进到投票队列中
        //需要对新进来的Vote信息的number进行校验，如果在比prepre阶段靠后的阶段中，已经出现了认证OK的同number的vote，则拒绝进入该队列
        if (dglPrepareMsgQueue.otherConfirm(hash, dglVotePreMsg.getNumber())) {
            logger.info("拒绝进入Prepare阶段，hash为{}", hash);
            return;
        }
        // 检测脚本是否正常
        try {
            for (Block block : dglVotePreMsg.getDglBlock().getBlockList()) {
                sqliteManager.tryExecute(block);
            }
        } catch (Exception e) {
            // 执行异常
            logger.info("sql指令预执行失败");
            e.printStackTrace();
            return;
        }

        //存入Pre集合中
        dglVotePreMsgConcurrentHashMap.put(hash, dglVotePreMsg);

        //加入Prepare行列，推送给所有人
        DGLVoteMsg dglPrepareMsg = new DGLVoteMsg();
        BeanUtil.copyProperties(dglVotePreMsg, dglPrepareMsg);
        dglPrepareMsg.setVoteType(MsgType.DLGPREPARE);
        dglPrepareMsg.setAppId(AppId.getValue());
        logger.error("DGLPreMsgQueue广播dglPrepareMsg消息");
        eventPublisher.publishEvent(new DGLMsgPrepareEvent(dglPrepareMsg));
    }

    /**
     * 根据hash，得到内存中的Block信息
     *
     * @param hash hash
     * @return Block
     */
    public List<Block> findByHash(String hash) {
        DGLVotePreMsg dglVotePreMsg = dglVotePreMsgConcurrentHashMap.get(hash);
        if (dglVotePreMsg != null) {
            return dglVotePreMsg.getDglBlock().getBlockList();
        }
//        logger.warn("DGLVotePreMsg消息为空{}", dglVotePreMsg);
        return null;
    }

    public DGLVotePreMsg findPreMsgByHash(String hash) {
        DGLVotePreMsg dglVotePreMsg = dglVotePreMsgConcurrentHashMap.get(hash);
        if (dglVotePreMsg != null) {
            return dglVotePreMsg;
        }
        return null;
    }

    /**
     * 新区块生成后，clear掉map中number比区块小的所有数据
     */
    @Order(3)
    @EventListener(DGLAddBlocksEvent.class)
    public void blockGenerated(DGLAddBlocksEvent dglAddBlocksEvent) {
        DGLBlock dglblock = (DGLBlock) dglAddBlocksEvent.getSource();
        int number = dglblock.getDglBlockHeader().getNumber();
        TimerManager.schedule(() -> {
            for (Map.Entry<String, DGLVotePreMsg> entry : dglVotePreMsgConcurrentHashMap.entrySet()) {
                if (entry.getValue().getNumber() <= number) {
                    dglVotePreMsgConcurrentHashMap.remove(entry.getKey());
                }
            }
            return null;
        }, 2000);
    }
}
